/* 
 * Copyright 2009 Shared Object (www.sharedobject.ch)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package edos.prm.activity;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import edos.prm.activity.Operation.PrmOperation;
import edos.prm.activity.Operation.Signature;
import edos.prm.engine.PRM;
import edos.prm.engine.PrmSecurityManager;
import edos.prm.role.RoleException;
import edos.prm.util.Time;
import edos.prm.util.VersionNumber;

/**
 * An {@code ActivityManager} is responsible for the control of all project
 * activities.
 * <p>
 * All activities are registered with the PRM information system. In order to
 * support interaction between activities, one activity queries the PRM for
 * another activity. Thus, the PRM is the point of coordinatin between the
 * set of activities in a project.
 * 
 * @version 1.0
 */
public interface ActivityManager extends Activity {
	
	/**
	 * Retreives the activity with the given name. 
	 * 
	 * @param name the String name of the activity being sought
	 * @return an activity with the specified name
	 */
	public Activity lookup(String name);
	
	/**
	 * Registers a new activity in the system.
	 * <p>
	 * The operations of an activity can only be invoked if the activity has
	 * been registered in the PRM information system.
	 * 
	 * @param activity the new activity to register
	 * @throws RoleException if the executing task does not have permission
	 * to call this operation.
	 */
	public void register(Activity activity) throws RoleException;
	
	/**
	 * Lists all registered activities
	 * 
	 * @return an array of registered activities
	 * @throws RoleException if the executing task does not have permission
	 * to call this operation.
	 */
	public Set<Activity> getRegisteredActivities() throws RoleException;
	
	/**
	 * Indicates if the denoted activity is registered
	 * 
	 * @param activity an activity of the project
	 * @return true if this activity has been registered
	 */
	public boolean isRegistered(Activity activity);
	
	/**
	 * Lists the operations associated with this activity
	 * 
	 * @param activity a project activity
	 * @return an array of operations.
	 * @throws RoleException if the executing task does not have permission
	 * to call this operation.
	 */
	public <T extends Operation> Set<T> getOperations(Activity activity)
	throws RoleException;
	
	/**
	 * Get an operation of the activity with the specified name
	 * 
	 * @param activity a project activity
	 * @param name the name of the operation being sought
	 * @return the operation with the specified name.
	 * @throws IllegalArgumentException if no operation with the specified name
	 * exists.
	 */
	public Operation getOperation(Activity activity, String name)
	throws IllegalArgumentException;

	/**
	 * The default Java implementation of the {@code ActivityManager}.
	 */
	public final class PrmActivityManager implements ActivityManager {

		private Time creationDate;
		private HashMap<Activity, HashSet<PrmOperation>> operationMap;		
		private HashSet<Activity> activities;
		private static PrmActivityManager instance;
		
		static {
			instance = new PrmActivityManager();
			instance.register(instance);
		}
		
		public static PrmActivityManager getInstance() {
			return instance;
		}
		
		private PrmActivityManager() {
			this.creationDate = new Time();
			activities = new HashSet<Activity>();
			this.operationMap = new HashMap<Activity, HashSet<PrmOperation>>();
		}
		
		public Activity lookup(String name) {
			name = name.replaceAll("var_", "");
			for ( Activity a : this.activities )
				if ( a.getClass().getName().endsWith(name) )
					return a;
			return null;
		}
		
		public Set<Activity> getRegisteredActivities() {
			PrmSecurityManager.roleCheck("getRegisteredActivities", getClass());
			HashSet<Activity> result = new HashSet<Activity>();
			result.addAll(activities);
			return result;
		}

		public Set<Operation> getOperations(Activity activity) {
			HashSet<PrmOperation> opSet = this.operationMap.get(activity);
			HashSet<Operation> result = new HashSet<Operation>();
			for ( PrmOperation po : opSet )
				result.add((Operation)po);
			return result;
		}

		public void register(Activity activity) {
			PrmSecurityManager.roleCheck("register", getClass(), Activity.class);

			Method[] methods = activity.getClass().getDeclaredMethods();
			HashSet<PrmOperation> ops = new HashSet<PrmOperation>();
			for ( Method method : methods ) {
				String name = method.getName();
				Signature sig = new Signature(method.getReturnType(),
						method.getParameterTypes());
				PrmOperation po = new PrmOperation(activity, name, sig);
				PrmOperation.registerOperation(po);
				ops.add(po);
			}
			this.operationMap.put(activity, ops);
			this.activities.add(activity);
		}

		public boolean isRegistered(Activity activity) {
			PrmSecurityManager.roleCheck("isRegistered", getClass(), Activity.class);
			return this.activities.contains(activity);
		}

		public Time getCreationDate() {
			return this.creationDate;
		}

		public String getDescription() {
			return "Default PRM Implementation of the activity manager.";
		}

		public VersionNumber getVersion() {
			return PRM.getPRMVersion();
		}

		public Operation getOperation(Activity activity, String name)
				throws RoleException, IllegalArgumentException {
			Set<Operation> ops = this.getOperations(activity);
			for ( Operation op : ops )
				if ( op.getName().equals(name) )
					return op;
			throw new IllegalArgumentException();
		}		
	}
}
